package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.common.results.ResultCodeEnum;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendItemMapper;
import com.atguigu.srb.core.pojo.vo.InvestVO;
import com.atguigu.srb.core.service.*;
import com.atguigu.srb.core.util.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 标的出借记录表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2022-10-24
 */
@Slf4j
@Service
public class LendItemServiceImpl extends ServiceImpl<LendItemMapper, LendItem> implements LendItemService {
    @Autowired
    UserInfoService userInfoService;
    @Autowired
    LendService lendService;
    @Autowired
    UserAccountService userAccountService;
    @Autowired
    TransFlowService transFlowService;

    @Override
    public String commitInvest(InvestVO investVO, String token) {
        //1、判断用户的状态：是否可以投资
        UserInfo userInfo = userInfoService.checkAccount(token);
        //2、判断标的的状态是否可以投资、投资金额是否超出标的剩余的可投资金额、用户投标金额是否超过自己账户余额
        String lendId = investVO.getLendId();//投标的标的id
        Lend lend = lendService.getById(lendId);
        if (lend.getStatus() != 1) {//不是募资中的标的不可以投标
            throw new BusinessException(ResultCodeEnum.LEND_INVEST_ERROR);
        }
        //本次投标金额+标的已收到的投标金额 > 标的总金额
        if (investVO.getInvestAmount().add(lend.getInvestAmount()).compareTo(lend.getAmount()) == 1) {
            //已满标
            throw new BusinessException(ResultCodeEnum.LEND_FULL_SCALE_ERROR);
        }
        //账户余额小于 投标金额
        if (userAccountService.getAmount(token).compareTo(investVO.getInvestAmount()) == -1) {
            throw new BusinessException(ResultCodeEnum.NOT_SUFFICIENT_FUNDS_ERROR);
        }
        //3、保存一条带支付的投资记录
        LendItem lendItem = new LendItem();
        //投资编号
        lendItem.setLendItemNo(LendNoUtils.getLendItemNo());
        lendItem.setLendId(lend.getId());
        //投资人id
        lendItem.setInvestUserId(userInfo.getId());
        lendItem.setInvestName(userInfo.getName());
        //投资金额
        lendItem.setInvestAmount(investVO.getInvestAmount());
        lendItem.setLendYearRate(lend.getLendYearRate());
        lendItem.setInvestTime(new Date());
        //开始计算收益的时间: 标的中的开始时间
        lendItem.setLendStartDate(lend.getLendStartDate());
        lendItem.setLendEndDate(lend.getLendEndDate());
        //预期收益：根据标的的还款方式计算收益
        BigDecimal expectAmount = new BigDecimal("0");
        switch (lend.getReturnMethod()) {
            case 1:
                expectAmount = Amount1Helper.getInterestCount(investVO.getInvestAmount(),
                        lend.getLendYearRate(),lend.getPeriod());
                break;
            case 2:
                expectAmount = Amount2Helper.getInterestCount(investVO.getInvestAmount(),
                        lend.getLendYearRate(),lend.getPeriod());
                break;
            case 3:
                expectAmount = Amount3Helper.getInterestCount(investVO.getInvestAmount(),
                        lend.getLendYearRate(),lend.getPeriod());
                break;
            case 4:
                expectAmount = Amount4Helper.getInterestCount(investVO.getInvestAmount(),
                        lend.getLendYearRate(),lend.getPeriod());
                break;

        }
        lendItem.setExpectAmount(expectAmount);
        lendItem.setRealAmount(new BigDecimal("0"));
        lendItem.setStatus(0);//0表示lend_item 是待支付状态
        this.save(lendItem);
        //4、访问hfb的投资接口： 将hfb该用户的账户余额扣除掉本次投资的金额(所有的金钱操作都在hfb操作后 由汇付宝通知我们是否成功)
        //生成自动访问hfb投资接口的表单返回给前端
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        //投资人在hfb中绑定的协议号：
        params.put("voteBindCode",userInfo.getBindCode());
        //借款人绑定的协议号：
        Long borrowUserId = lend.getUserId(); //生成标的的借款人的id
        UserInfo borrowUserInfo = userInfoService.getById(borrowUserId);
        params.put("benefitBindCode",borrowUserInfo.getBindCode());
        //项目编号：一个投资就是购买标的的一个记录
        //agent_project_code
        params.put("agentProjectCode",lend.getLendNo());
        //项目名称：标的的名称
        params.put("agentProjectName",lend.getTitle());
        //本地订单的编号： 可以使用lend_item的编号  为了查找唯一的一个lend_item
        params.put("agentBillNo",lendItem.getLendItemNo());
        //投资金额
        params.put("voteAmt",investVO.getInvestAmount());
        params.put("votePrizeAmt",new BigDecimal("0"));
        //商户手续费
        params.put("voteFeeAmt",new BigDecimal("0"));
        // 必须设置，否则hfb服务出现空指针
        params.put("projectAmt", lend.getAmount()); //标的总金额
        params.put("notifyUrl",HfbConst.INVEST_NOTIFY_URL);
        params.put("returnUrl",HfbConst.INVEST_RETURN_URL);
        params.put("timestamp",System.currentTimeMillis());
        //签名
        params.put("sign", RequestHelper.getSign(params));
        String form = FormHelper.buildForm(HfbConst.INVEST_URL, params);
        return form;
    }

    @Override
    public String investNotify(HttpServletRequest request) {
        //1、获取参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        Map<String, Object> map = RequestHelper.switchMap(parameterMap);
        //2、验证签名
        if(!RequestHelper.isSignEquals(map)){
            log.error("投资回调,签名验证失败:{}" , JSON.toJSONString(map));
            return "fail";
        }
        //判断hfb处理业务是否成功：
        String resultCode = map.get("resultCode").toString();
        if(!StringUtils.equals(resultCode,"0001")){
            log.error("投资回调,响应失败:{}" , JSON.toJSONString(map));
            return "fail";
        }
        try {
            //3、根据回调的参数：如果投资人支付成功
            //3.1 修改标的的投资人数量+已投金额
            //获取已投金额
            String voteAmt = map.get("voteAmt").toString();
            //获取标的的lendNo: 查找lend标的
            String agentProjectCode = map.get("agentProjectCode").toString();
            //获取标的
            Lend lend = lendService.getOne(new LambdaQueryWrapper<Lend>()
                    .eq(Lend::getLendNo, agentProjectCode));
            lend.setInvestNum(lend.getInvestNum()+1);
            lend.setInvestAmount(lend.getInvestAmount().add(new BigDecimal(voteAmt)));
            //更新标的
            lendService.update(new LambdaUpdateWrapper<Lend>()
                        .eq(Lend::getId , lend.getId())
                        .set(Lend::getInvestNum , lend.getInvestNum())
                        .set(Lend::getInvestAmount , lend.getInvestAmount()));
            //3.2 修改lend_item的支付状态
            //获取lend_item_no:
            String agentBillNo = map.get("agentBillNo").toString();
            LendItem lendItem = this.getOne(new LambdaQueryWrapper<LendItem>()
                    .eq(LendItem::getLendItemNo, agentBillNo));
            lendItem.setStatus(1);
            this.update(new LambdaUpdateWrapper<LendItem>()
                    .eq(LendItem::getId , lendItem.getId())
                    .set(LendItem::getStatus , lendItem.getStatus()));
            //3.3 修改投资人账户余额+ 冻结金额
            //hfb投资人绑定的协议号：可以查询到唯一的一个用户
            String voteBindCode = map.get("voteBindCode").toString();
            //根据bindcode查询投资人userId，根据userId更新用户的一个可用的账户余额
            UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>()
                    .eq(UserInfo::getBindCode, voteBindCode));
            //查询用户的一个可用的账户
            UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>()
                    .eq(UserAccount::getUserId, userInfo.getId())
                    .eq(UserAccount::getStatus, 1)
                    .last("limit 1"));
            userAccount.setAmount(userAccount.getAmount().subtract(new BigDecimal(voteAmt)));
            userAccount.setFreezeAmount(userAccount.getFreezeAmount().add(new BigDecimal(voteAmt)));
            userAccountService.update(new LambdaUpdateWrapper<UserAccount>()
                .eq(UserAccount::getId , userAccount.getId())
                .set(UserAccount::getAmount , userAccount.getAmount())
                .set(UserAccount::getFreezeAmount , userAccount.getFreezeAmount()));
            //保存回调的交易日志：
            TransFlow transFlow = new TransFlow();
            transFlow.setUserId(userInfo.getId());
            transFlow.setUserName(userInfo.getName());
            //交易类型（1：充值 2：提现 3：投标 4：投资回款 ...）
            transFlow.setTransType(3);
            transFlow.setTransTypeName("投标");
            transFlow.setTransNo(agentBillNo);
            transFlow.setTransAmount(new BigDecimal(voteAmt));
            transFlow.setMemo(JSON.toJSONString(map));
            transFlowService.save(transFlow);

            return "success";
        } catch (Exception e) {
            log.error("投资回调异常:{}", ExceptionUtils.getStackTrace(e));
            return "fail";
        }


    }
     /*
           如果标的已经满标，管理员可以放款：

                1、生成还款计划：
                    借款人每期应还款的信息：
                2、生成回款计划：
                    投资人每期根据自己投资情况应回款的信息：
         */

    //用户在hfb确认投资后的回调接口：
    //1、更新lend_item的状态
    //2、更新本地投资人的账户余额( 扣除账户余额  增加锁定资金金额 )
}
